using System.Net;
using System.Threading;
using Lidgren.Network;

namespace _3DTicTacToe.Net
{
    sealed class Connection
    {
        private readonly NetPeer _peer;
        private readonly Thread _listenThread;

        public delegate void MsgDlg(NetMessage message);
        public delegate void MsgDlg2();

        public event MsgDlg OnMessageReceived;
        public event MsgDlg OnChatMessageReceived;
        public event MsgDlg OnPlayerInfoReceived;
        public event MsgDlg OnGameTurnReceived;

        public event MsgDlg2 OnPlayerInfoAckReceived;
        public event MsgDlg2 OnGameStarts;

        public event MsgDlg2 OnDisconnected;

        bool _shutdownDone;

        public bool IsHost
        {
            get; private set;
        }

        private void HandleIncomingMsg(string data)
        {
            var msg = NetMessage.Deserialize(data);
            switch (msg.Header)
            {
                case "chat":
                    Log.Add("Chatmessage received", "TConnection");
                    if (OnChatMessageReceived != null) OnChatMessageReceived(msg);
                    break;
                case "player-info":
                    Log.Add("Player info received", "TConnection");
                    if (OnPlayerInfoReceived != null) OnPlayerInfoReceived(msg);
                    break;
                case "turn":
                    Log.Add("Game turn received", "TConnection");
                    if (OnGameTurnReceived != null) OnGameTurnReceived(msg);
                    break;
                case "ack-player-info":
                    Log.Add("Received acknowledgement for sent player info", "TConnection");
                    if (OnPlayerInfoAckReceived != null) OnPlayerInfoAckReceived();
                    break;
                case "game-starting":
                    Log.Add("Game appears to start...");
                    if (OnGameStarts != null) OnGameStarts();
                    break;
                default:
                    Log.Add("Unknown message received. Header is: " + msg.Header, "TConnection");
                    if (OnMessageReceived != null) OnMessageReceived(msg);
                    break;
            }
        }

        private void HandleStatusChange(NetConnectionStatus status)
        {
            switch(status)
            {
                case NetConnectionStatus.Disconnected:
                    Shutdown();
                    //if (OnDisconnected != null) OnDisconnected();
                    break;
            }
        }

        private void Listen()
        {
            while(true)
            {
                NetBuffer buf = _peer.CreateBuffer();
                NetConnection sender;
                NetMessageType type;

                _peer.ReadMessage(buf, out type, out sender);

                switch(type)
                {
                    case NetMessageType.StatusChanged:
                        HandleStatusChange(sender.Status);
                        break;

                    case NetMessageType.Data:
                        HandleIncomingMsg(buf.ReadString());
                        break;
                }

                Thread.Sleep(50);
            }
        }

        private Connection()
        {
            IsHost = false;

            var config = new NetConfiguration("3dtictactoe") {MaxConnections = 1};
            _peer = new NetPeer(config);

            _listenThread = new Thread(Listen);
        }

        private Connection(int port)
        {
            IsHost = true;

            var config = new NetConfiguration("3dtictactoe") {MaxConnections = 1, Port = port};
            _peer = new NetPeer(config);

            _listenThread = new Thread(Listen);
        }

        #region public interface

        public void Start()
        {
            _peer.Start();
            _listenThread.Start();
        }

        public void Connect(string host, int port)
        {
            _listenThread.Start();
            _peer.Connect(host, port);
        }

        public void Shutdown()
        {
            if (_shutdownDone)
                return;

            _listenThread.Abort();
            _peer.Shutdown("123");
            _shutdownDone = true;
        }

        public void SendMessage(NetMessage msg)
        {
            var data = msg.Serialize();
            var buf = _peer.CreateBuffer(data);

            _peer.SendToAll(buf, NetChannel.ReliableInOrder1);
        }

        // static 

        public static Connection CreateServer(int port)
        {
            var connection = new Connection(port);
            return connection;
        }

        public static Connection CreateClient()
        {
            var connection = new Connection();
            return connection;
        }

        #endregion
    }
}